package com.zimin.cloud.system.biz.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zimin.cloud.file.api.IGridFsFeign;
import com.zimin.cloud.mine.pojo.file.business.FaceBO;
import com.zimin.cloud.mine.pojo.system.business.AccountBO;
import com.zimin.cloud.mine.pojo.system.business.LoginBO;
import com.zimin.cloud.mine.pojo.system.view.AccountVO;
import com.zimin.cloud.mine.pojo.system.view.LoginVO;
import com.zimin.cloud.system.biz.IAccountBiz;
import com.zimin.cloud.system.biz.ILoginInfoBiz;
import com.zimin.cloud.system.pojo.entity.Account;
import com.zimin.cloud.system.service.IAccountPostService;
import com.zimin.cloud.system.service.IAccountRoleService;
import com.zimin.cloud.system.service.IRoleMenuService;
import com.zimin.cloud.system.service.impl.AccountServiceImpl;
import com.zimin.common.annotation.Biz;
import com.zimin.common.enums.FaceVerifyType;
import com.zimin.common.exception.GraceException;
import com.zimin.common.utils.*;
import com.zimin.redis.constant.authorize.AuthorizeRedisConstant;
import com.zimin.redis.utils.RedisOperator;
import com.zimin.shared.enums.ResponseStatusEnum;
import com.zimin.shared.pojo.PageVO;
import com.zimin.shared.pojo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;


/**
 * <p>
 * 用户信息 业务对象
 * </p>
 *
 * @author zimin
 * @since 2021-12-30 11:04
 */
@Biz("accountBiz")
public class AccountBizImpl extends AccountServiceImpl implements IAccountBiz {

    @Autowired
    private IAccountPostService accountPostService;
    @Autowired
    private IAccountRoleService accountRoleService;
    @Autowired
    private IRoleMenuService roleMenuService;
    @Autowired
    private RedisOperator redisOperator;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private ILoginInfoBiz loginInfoBiz;
    @Autowired
    private FaceVerifyUtils faceVerifyUtils;
    @Autowired
    private IGridFsFeign gridFsFeign;

    @Override
    public PageVO<AccountVO> pageAccountList(Page<Account> accountPage, AccountBO accountBO) {
        var accountPageList = page(accountPage, accountBO);
        return pageToPageVO(accountPageList, BeanUtils.listEntityToView(accountPage.getRecords(), AccountVO.class));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(AccountBO accountBO) {
        accountBO.setPassword(Md5Util.encrypt((accountBO.getPassword())));
        var account = BeanUtils.coverClass(accountBO, Account.class);
        account.setCreateTime(LocalDateTime.now());
        var accountSaveFlag = super.save(account);
        accountBO.setId(account.getId());
        if (accountSaveFlag && StringUtils.isNotEmpty(accountBO.getPostId())) {
            //关联账号岗位
            accountPostService.save(accountBO);
        }
        if (accountSaveFlag && StringUtils.isNotEmpty(accountBO.getRoleId())) {
            //关联账号角色
            accountRoleService.save(accountBO);
        }
        return accountSaveFlag;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(AccountBO accountBO) {
        if (StringUtils.isNotEmpty(accountBO.getRoleId())) {
            if (!accountRoleService.update(accountBO)) {
                accountRoleService.save(accountBO);
            }
        }
        if (StringUtils.isNotEmpty(accountBO.getPostId())) {
            if (!accountPostService.update(accountBO)) {
                accountPostService.save(accountBO);
            }
        }
        accountBO.setPassword(Md5Util.encrypt((accountBO.getPassword())));
        return super.updateById(BeanUtils.coverClass(accountBO, Account.class));
    }

    @Override
    public boolean resetPassword(AccountBO accountBO) {
        GraceException.isNull(accountBO.getId(), ResponseStatusEnum.ACCOUNT_ID_IS_NULL);
        GraceException.isNull(accountBO.getPassword(), ResponseStatusEnum.ACCOUNT_PASSWORD_IS_NULL);
        return updateById(accountBO);
    }

    @Override
    public LoginVO login(LoginBO loginBO) {
        var accountBO =
                AccountBO.builder().accountName(loginBO.getUserName()).password(Md5Util.encrypt((loginBO.getPassword()))).build();
        var accountVO = super.isExistByUserNameAndPwd(accountBO);
        GraceException.isNull(accountVO, ResponseStatusEnum.LOGIN_NAME_OR_PWD);
        return doLoginAfter(accountVO);
    }

    @Override
    public LoginVO faceLogin(LoginBO loginBO) throws Exception {
        GraceException.isNull(loginBO.getUserName(), ResponseStatusEnum.ACCOUNT_USERNAME_IS_NULL);
        GraceException.isNull(loginBO.getImg64(), ResponseStatusEnum.FACE_IS_NULL);
        var accountVO = super.isExistByUserNameAndPwd(AccountBO.builder().accountName(loginBO.getUserName()).build());
        var faceId = accountVO.getFaceId();
        GraceException.isNull(faceId, ResponseStatusEnum.FACE_IS_NOT_SET);
        //根据文件Id获取Base64
        var result = gridFsFeign.readFace64InGridFs(faceId);
        GraceException.isFalse(result.getSuccess(), ResponseStatusEnum.FACE_IS_NOT_SET);
        var faceBase64 = result.getData().toString();
        var flag = faceVerifyUtils.faceVerifyV2(FaceVerifyType.BASE64.getType(), loginBO.getImg64(), faceBase64, 80);
        GraceException.isFalse(flag, ResponseStatusEnum.FACE_VERIFY_LOGIN_ERROR);
        return doLoginAfter(accountVO);
    }

    @Override
    public LoginVO getLoginInfoById(String userId, String token) {
        return getLoginInfo(userId, token);
    }

    @Override
    public boolean updatePassword(AccountBO accountBO) {
        var newPassword = accountBO.getNewPassword();
        var confirmNewPassword = accountBO.getConfirmNewPassword();
        GraceException.isNull(accountBO.getId(), ResponseStatusEnum.ACCOUNT_ID_IS_NULL);
        var account = getById(accountBO.getId());
        GraceException.isNull(account, ResponseStatusEnum.ACCOUNT_ID_IS_NULL);
        var oldPassword = accountBO.getOldPassword();
        GraceException.isNull(oldPassword, ResponseStatusEnum.ACCOUNT_PASSWORD_IS_NULL);
        GraceException.isFalse(account.getPassword().equals(oldPassword), ResponseStatusEnum.PASSWORD_IS_ERROR);
        GraceException.isNull(newPassword, ResponseStatusEnum.ACCOUNT_PASSWORD_IS_NULL);
        GraceException.isNull(confirmNewPassword, ResponseStatusEnum.ACCOUNT_PASSWORD_IS_NULL);
        GraceException.isFalse(newPassword.equals(confirmNewPassword), ResponseStatusEnum.NEW_AND_CONFIRM_PASSWORD_NO_EQUAL);
        return update(Wrappers.<Account>lambdaUpdate()
                .set(Account::getPassword, Md5Util.encrypt((accountBO.getPassword())))
                .eq(Account::getId, accountBO.getId()));
    }

    @Override
    public boolean setFaceId(FaceBO faceBO) throws Exception {
        var result = gridFsFeign.uploadFace(faceBO);
        GraceException.isFalse(result.getSuccess(), ResponseStatusEnum.FACE_UPLOAD_FAIL);
        var faceId = result.getData();
        return update(Wrappers.<Account>lambdaUpdate()
                .set(Account::getFaceId, faceId)
                .eq(Account::getId, faceBO.getUserId()));
    }


    /**
     * <p>登录成功的后置处理</p>
     *
     * @param accountVO
     * @return com.zimin.cloud.mine.pojo.system.view.LoginVO
     * @since 2022-04-08 10:34:56
     */
    private LoginVO doLoginAfter(AccountVO accountVO) {
        var token = IdUtils.upperUUID();
        var loginVO = LoginVO.builder().token(token).accountId(accountVO.getId()).build();
        var loginVoCompletableFuture = CompletableFuture.supplyAsync(() -> loginVO);
        var accountRole = loginVoCompletableFuture.thenAccept(l -> l.setRolePermissions(accountRoleService.getRolePermissions(accountVO.getId())));
        var roleMenu = loginVoCompletableFuture.thenAccept(l -> l.setButtonPermissions(roleMenuService.getMenuPermissions(accountVO.getId())));
        var accountVo = loginVoCompletableFuture.thenAccept(l -> l.setAccountVO(accountVO));
        CompletableFuture.allOf(accountRole, roleMenu, accountVo).join();
        checkIsLogin(accountVO);
        //缓存token
        redisOperator.set(String.format(AuthorizeRedisConstant.REDIS_LOGIN_TOKEN, accountVO.getId()), token, AuthorizeRedisConstant.TWELVE_EXPIRE_TIME);
        //缓存登录信息
        redisOperator.set(String.format(AuthorizeRedisConstant.REDIS_LOGIN_INFO, token), JSON.toJSONString(accountVO), AuthorizeRedisConstant.TWELVE_EXPIRE_TIME);
        //缓存角色信息
        redisOperator.set(String.format(AuthorizeRedisConstant.REDIS_LOGIN_ROLE, token), JSON.toJSONString(loginVO.getRolePermissions()), AuthorizeRedisConstant.TWELVE_EXPIRE_TIME);
        //缓存权限信息
        redisOperator.set(String.format(AuthorizeRedisConstant.REDIS_LOGIN_PERMISSIONS, token), JSON.toJSONString(loginVO.getButtonPermissions()), AuthorizeRedisConstant.TWELVE_EXPIRE_TIME);
        //保存登录日志
        threadPoolExecutor.submit(() -> {
            loginInfoBiz.save(loginVO);
        });
        return loginVO;
    }

    /**
     * <p>如果之前已经登录过，需要删除redis缓存释放资源</p>
     *
     * @param accountVO 登录对象
     * @return void
     * @since 2022-03-24 11:03:17
     */
    private void checkIsLogin(AccountVO accountVO) {
        var oldToken = redisOperator.get(String.format(AuthorizeRedisConstant.REDIS_LOGIN_TOKEN, accountVO.getId()));
        if (ObjectUtils.isNotEmpty(oldToken)) {
            redisOperator.del(String.format(AuthorizeRedisConstant.REDIS_LOGIN_INFO, oldToken));
            redisOperator.del(String.format(AuthorizeRedisConstant.REDIS_LOGIN_ROLE, oldToken));
            redisOperator.del(String.format(AuthorizeRedisConstant.REDIS_LOGIN_PERMISSIONS, oldToken));
        }
    }

    /**
     * <p>获取登录信息</p>
     *
     * @param userId
     * @param token  登录凭证
     * @return com.zimin.cloud.mine.pojo.system.view.LoginVO
     * @since 2022-04-18 10:18:27
     */
    private LoginVO getLoginInfo(String userId, String token) {
        //1、校验登录凭证是否有效
        var oldToken = redisOperator.get(String.format(AuthorizeRedisConstant.REDIS_LOGIN_TOKEN, userId));
        GraceException.isNull(oldToken, ResponseStatusEnum.TOKEN_EXPIRES);
        GraceException.isFalse(token.equals(oldToken), ResponseStatusEnum.TOKEN_EXPIRES);
        //2、获取对象信息
        var loginInfo = redisOperator.get(String.format(AuthorizeRedisConstant.REDIS_LOGIN_INFO, oldToken), String.class);
        var rolePermissions = redisOperator.get(String.format(AuthorizeRedisConstant.REDIS_LOGIN_ROLE, oldToken), String.class);
        var permissions = redisOperator.get(String.format(AuthorizeRedisConstant.REDIS_LOGIN_PERMISSIONS, oldToken), String.class);
        return LoginVO.builder().accountVO(JSON.parseObject(loginInfo, AccountVO.class))
                .rolePermissions(JSON.parseArray(rolePermissions, String.class))
                .buttonPermissions(JSON.parseArray(permissions, String.class))
                .build();
    }

}
